#  $Id: fields.py 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
#  Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
#  
#  Permission to use, copy, modify, and distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
# 
#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
from django.db import models

class Enum(object):
   def __init__(self, value, slug, display = None):
      super(Enum, self).__init__()
      self.value = None
      self.slug = None
      self.display = None

      if isinstance(value, int):
         self.value = value
      else:
         raise TypeError, 'item value should be int not %s' %stdlib.my_name(
               value)

      if isinstance(slug, str):
         self.slug = slug
      else:
         raise TypeError, 'item slug should be str not %s' %stdlib.my_name(slug)

      if isinstance(display, str):
         self.display = display
      elif isinstance(display, None):
         self.display = slug
      else:
         raise TypeError, 'item slug should be str no %s' %stdlib.my_name(
               display)

   def __str__(self):
      return self.display.encode('utf-8')

   def __repr__(self):
      return str(self)

   def __eq__(self, other):
      if isinstance(other, Enum):
         return self.value == other.value

      if isinstance(other, (int, str, unicode)):
         try:
            return self.value == int(other)
         except ValueError:
            return False

      return False

   def __ne__(self, other):
      return not self.__eq__(other)

class Enumeration(object):
   @classmethod
   def from_value(cls, value):
      retval = None
      for attr in cls.__dict__.values():
         if isinstance(attr, Enum) and attr.value == value:
            retval = attr
            break

      return retval

   @classmethod
   def from_slug(cls, slug):
      retval = None
      for attr in cls.__dict__.values():
         if isinstance(attr, Enum) and attr.slug == slug:
            retval = attr
            break

      return retval

   @classmethod
   def get_items(cls):
      items = filter(lambda attr: isinstance(attr, Enum), cls.__dict__.values())
      items.sort(lambda x, y: cmp(x.value, y.value))
      return items

   @classmethod
   def get_choices(cls):
      return [(
         item.value, item.display.capitalize()) for item in cls.get_items()]

class EnumField(models.Field):
   __metaclass__ = models.SubfieldBase

   def __init__(self, enumeration, *args, **kwargs):
      kwargs.setdefault('choices', enumeration.get_choices())
      super(EnumField, self).__init__(*args, **kwargs)
      self.enumeration = enumeration
    
   def get_internal_type(self):
      return 'IntegerField'

   def to_python(self, value):
      if value == None or value == '' or value == u'':
         return None
      
      if isinstance(value, Enum):
         return value
      
      if isinstance(value, (int, long)) or isinstance(value, (str, unicode)):
         item = self.enumeration.from_value(int(value))
         if item:
            return item

      raise ValueError, '%s is not a valid value for the enum field' % value
    
    
   def get_db_prep_save(self, value, connection):
      if value:
         return value.value
    
   def get_db_prep_lookup(self, lookup_type, value, connection,
         prepared = False):
      def prepare(value):
         if value == None:
            return None
         
         if isinstance(value, (int, str, unicode)):
            try:
               return int(value)
            except ValueError:
               raise ValueError(
                     'invalid value for the enum field lookup: %r' % value)

            if isinstance(value, Enum):
                return value.value
        
      if lookup_type == 'exact':
         return [prepare(value)]
      elif lookup_type == 'in':
         return [prepare(v) for v in value]
      elif lookup_type == 'isnull':
         return []
      else:
         raise TypeError('Lookup type %r not supported.' % lookup_type)
